#include "config.h"

#include <dirent.h>
#include <sys/types.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <stdarg.h>
#include <signal.h>

#define DBG_SUBSYS S_LIBTASK

#include "sysy_lib.h"
#include "cache.h"
#include "get_version.h"
#include "vnode.h"
#include "cluster.h"
#include "volume.h"
#include "lichstor.h"
#include "stor_root.h"
#include "lich_md.h"
#include "md_map.h"
#include "configure.h"
#include "job_dock.h"
#include "chunk.h"
#include "rmsnap_bh.h"
#include "ynet_rpc.h"
#include "net_global.h"
#include "ylog.h"
#include "dbg.h"
#include "lichstor.h"
#include "timer.h"
#include "system.h"

static worker_handler_t rmsnap_bh_handler;
static int __running__ = 0;

int rmsnap_bh_root(const char *pool, fileid_t *rootid)
{
        int ret;

        ret = stor_root(pool, RMSNAP_ROOT, rootid);
        if (unlikely(ret))
                GOTO(err_ret, ret);

        return 0;
err_ret:
        return ret;
}

int rmsnap_bh_create(const char *pool, const fileid_t *fileid, const char *snap)
{
        int ret;
        fileid_t rootid;
        char name[MAX_NAME_LEN];

        ret = rmsnap_bh_root(pool, &rootid);
        if (ret)
                GOTO(err_ret, ret);

        snprintf(name, MAX_NAME_LEN, CHKID_FORMAT":%s", CHKID_ARG(fileid), snap);
        ret = stor_mkvol(&rootid, name, NULL, NULL);
        if (ret)
                GOTO(err_ret, ret);

        return 0;
err_ret:
        return ret;
}

int rmsnap_bh_remove(const char *pool, const fileid_t *fileid, const char *snap)
{
        int ret;
        fileid_t rootid;
        char name[MAX_NAME_LEN];

        ANALYSIS_BEGIN(0);

        ret = rmsnap_bh_root(pool, &rootid);
        if (ret)
                GOTO(err_ret, ret);

        if (fileid) {
                snprintf(name, MAX_NAME_LEN, CHKID_FORMAT":%s", CHKID_ARG(fileid), snap);
        } else {
                snprintf(name, MAX_NAME_LEN, "%s", snap);
        }
        ret = stor_rmvol(&rootid, name, 0);
        if (ret)
                GOTO(err_ret, ret);

        ANALYSIS_END(0, IO_WARN, NULL);

        return 0;
err_ret:
        return ret;
}

int rmsnap_bh_init()
{
        return 0;
}

STATIC int __pool_rmsnap_bh_worker(void *pool, void *_arg)
{
        int ret, delen, success = 0, retry = 0;
        uint64_t offset = 0, offset2 = 0;
        char buf[BIG_BUF_LEN], uuid[MAX_NAME_LEN] = {};
        struct dirent *de;
        fileid_t rootid;

        (void) _arg;

        DBUG("pool %s\n", (const char *)pool);

retry0:
        ret = stor_root(pool, RMSNAP_ROOT, &rootid);
        if (unlikely(ret)) {
                if (ret == EAGAIN) {
                        USLEEP_RETRY(err_ret, ret, retry0, retry, 10, (1000 * 1000));
                } else
                        GOTO(err_ret, ret);
        }

        get_uuid(uuid);
        ret = md_listpool_open(&rootid, uuid);
        if (unlikely(ret))
                GOTO(err_ret, ret);

        while(1) {
retry:
                success = 0;
                delen = BIG_BUF_LEN;
                memset(buf, 0, delen);
                ret = md_listpool(&rootid, uuid, offset, buf, &delen);
                if (unlikely(ret)) {
                        USLEEP_RETRY(err_close, ret, retry, retry, 10, (1000 * 1000));
                }

                DBUG("task rmsnap delen %d\n", delen);

                if (delen == 0)
                        break;

                offset2 = 0;
                dir_for_each(buf, delen, de, offset2) {
                        DBUG("rmsnap_bh file %s\n", de->d_name);

                        if (strlen(de->d_name) == 0)
                                goto out;
                        else if (delen - offset2 < sizeof(*de) + MAX_NAME_LEN)
                                break;

                        offset += de->d_reclen;

                        if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..") ||
                                        de->d_name[0] == '.') {
                                continue;
                        }

                        // DINFO("rmsnap_bh file %s\n", de->d_name);

                        ret = volume_rmsnap_bh(&rootid, de->d_name);
                        if (unlikely(ret)) {
                                DWARN("rmsnap_bh %s fail ret %d\n", de->d_name, ret);
                                if (ret == ENOENT || ret == ENOKEY || ret == ECANCELED) {
                                        rmsnap_bh_remove(pool, NULL, de->d_name);
                                }

                                continue;
                        }

                        success++;
                }

#if 0
                if (success) {
                        DINFO("rmsnap retry\n");
                        goto retry;
                }
#endif
        }
out:
        /*ret = timer1_settime(&rmsnap_bh_handler, USEC_PER_MIN);
        if (unlikely(ret)) {
                DERROR("settime error ret %d\n", ret);
                YASSERT(0);
        }*/
        md_listpool_close(&rootid, uuid);

        return 0;
err_close:
        md_listpool_close(&rootid, uuid);
err_ret:
        DWARN("rmsnap_bh file error ret (%d) %s\n", ret, strerror(ret));
        SWARN(0, "%s, rmsnap_bh file error ret (%d) %s\n", M_DATA_VOLUME_WARN, ret, strerror(ret));

        /*ret = timer1_settime(&rmsnap_bh_handler, USEC_PER_MIN);
        if (unlikely(ret)) {
                DERROR("settime error ret %d\n", ret);
                YASSERT(0);
        }*/

        return ret;
}

STATIC int __rmsnap_bh_worker(void *_args)
{
        int ret;
        (void) _args;

        if (__running__ == 0) {
                DINFO("rmsnap bh stoped\n");
                return 0;
        }
        
        ret = system_pool_iterator(__pool_rmsnap_bh_worker, NULL);

        ret = timer1_settime(&rmsnap_bh_handler, USEC_PER_MIN);
        if (unlikely(ret)) {
                DERROR("settime error ret %d\n", ret);
                YASSERT(0);
        }

        return ret;
}

int rmsnap_bh_start()
{
        int ret;

#if 0
        DERROR("rmsnap_bh file service disabled\n");
        return 0;
#endif

        DINFO("rmsnap_bh file service start\n");

        __running__ = 1;
        
        ret = timer1_create(&rmsnap_bh_handler, "rmsnap_bh_file", __rmsnap_bh_worker, NULL);
        if (unlikely(ret))
                GOTO(err_ret, ret);

        ret = timer1_settime(&rmsnap_bh_handler, USEC_PER_MIN);
        if (unlikely(ret))
                GOTO(err_ret, ret);

        DINFO("rmsnap_bh file service started\n");
        return 0;
err_ret:
        return ret;
}

void rmsnap_bh_stop()
{
        __running__ = 0;
}
